package club.fuwenhao.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.http.util.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Map;
import java.util.Objects;

/**
 * @author fwh
 * @email fuwenhao594@163.com
 * @date 2022/1/30 9:27 上午
 */
public class HttpUtils {


    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    private HttpUtils() {
    }

    //多线程共享实例
    private static CloseableHttpClient httpClient;

    static {
//        SSLContext sslContext = createSSLContext();
        SSLContext sslContext = null;
        try {
            sslContext = createIgnoreVerifySSL();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
//        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
        // 注册http套接字工厂和https套接字工厂
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
//                .register("https", sslsf)
                .register("https", new SSLConnectionSocketFactory(sslContext))
                .build();
        // 连接池管理器
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connMgr.setMaxTotal(300);//连接池最大连接数
        connMgr.setDefaultMaxPerRoute(300);//每个路由最大连接数，设置的过小，无法支持大并发
        connMgr.setValidateAfterInactivity(5 * 1000); //在从连接池获取连接时，连接不活跃多长时间后需要进行一次验证
        // 请求参数配置管理器
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(60000)
                .setSocketTimeout(60000)
                .setConnectionRequestTimeout(60000)
                .build();
        // 获取httpClient客户端
        httpClient = HttpClients.custom()
                .setConnectionManager(connMgr)
                .setDefaultRequestConfig(requestConfig)
                .build();
    }

    /**
     * GET请求
     *
     * @param url
     * @return
     */
    public static String getUrl(String url) {
        return sendHttp(HttpMethod.GET, url, null, null);
    }

    /**
     * GET请求/带头部的信息
     *
     * @param url
     * @param header
     * @return
     */
    public static String getUrl(String url, Map<String, String> header) {
        return sendHttp(HttpMethod.GET, url, header, null);
    }

    /**
     * POST请求/无参数
     *
     * @param url
     * @return
     */
    public static String postJson(String url) {
        return postJson(url, null, null);
    }

    /**
     * POST请求/有参数
     *
     * @param url
     * @param param
     * @return
     */
    public static String postJson(String url, String param) {
        return postJson(url, null, param);
    }

    /**
     * POST请求/无参数带头部
     *
     * @param url
     * @param header
     * @return
     */
    public static String postJson(String url, Map<String, String> header) {
        return postJson(url, header, null);
    }

    /**
     * POST请求/有参数带头部
     *
     * @param url
     * @param header
     * @param params
     * @return
     */
    public static String postJson(String url, Map<String, String> header, String params) {
        return sendHttp(HttpMethod.POST, url, header, params);
    }

    /**
     * 上传文件流
     * @param url
     * @param header
     * @param param
     * @param fileName
     * @param inputStream
     * @return
     */
//    public static RequestResult postUploadFileStream(String url, Map<String, String> header, Map<String, String> param, String fileName, InputStream inputStream) {
//        byte[] stream = inputStream2byte(inputStream);
//        return postUploadFileStream(url, header, param, fileName, stream);
//    }

    /**
     * 上传文件
     * @param url 上传地址
     * @param header 请求头部
     * @param param 请求表单
     * @param fileName 文件名称
     * @param stream 文件流
     * @return
     */
//    public static RequestResult postUploadFileStream(String url, Map<String, String> header, Map<String, String> param, String fileName, byte[] stream) {
//        String infoMessage =  new StringBuilder().append("request postUploadFileStream，url:").append(url).append("，header:").append(header.toString()).append("，param:").append(JSONObject.toJSONString(param)).append("，fileName:").append(fileName).toString();
//        log.info(infoMessage);
//        RequestResult result = new RequestResult();
//        if(StringUtils.isBlank(fileName)){
//            log.warn("上传文件名称为空");
//            throw new RuntimeException("上传文件名称为空");
//        }
//        if(Objects.isNull(stream)){
//            log.warn("上传文件流为空");
//            throw new RuntimeException("上传文件流为空");
//        }
//        try {
//            ContentType contentType = ContentType.MULTIPART_FORM_DATA.withCharset("UTF-8");
//            HttpPost httpPost = new HttpPost(url);
//            if (Objects.nonNull(header) && !header.isEmpty()) {
//                for (Map.Entry<String, String> entry : header.entrySet()) {
//                    httpPost.setHeader(entry.getKey(), entry.getValue());
//                    if(log.isDebugEnabled()){
//                        log.debug(entry.getKey() + ":" + entry.getValue());
//                    }
//                }
//            }
//            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
//            builder.setCharset(Charset.forName("UTF-8"));//使用UTF-8
//            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);//设置浏览器兼容模式
//            if (Objects.nonNull(param) && !param.isEmpty()) {
//                for (Map.Entry<String, String> entry : param.entrySet()) {
//                    if (log.isDebugEnabled()) {
//                        log.debug(entry.getKey() + ":" + entry.getValue());
//                    }
//                    builder.addPart(entry.getKey(), new StringBody(entry.getValue(), contentType));
//                }
//            }
//            builder.addBinaryBody("file",  stream, contentType, fileName);//封装上传文件
//            httpPost.setEntity(builder.build());
//            //请求执行，获取返回对象
//            CloseableHttpResponse response = httpClient.execute(httpPost);
//            log.info("postUploadFileStream response status:{}", response.getStatusLine());
//            int statusCode = response.getStatusLine().getStatusCode();
//            if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_NO_CONTENT) {
//                result.setSuccess(true);
//            }
//            HttpEntity httpEntity = response.getEntity();
//            if (Objects.nonNull(httpEntity)) {
//                String content = EntityUtils.toString(httpEntity, "UTF-8");
//                log.info("postUploadFileStream response body:{}", content);
//                result.setMsg(content);
//            }
//            HttpClientUtils.closeQuietly(response);
//        } catch (Exception e) {
//            log.error(infoMessage + " failure", e);
//            result.setMsg("请求异常");
//        }
//        return result;
//    }

    /**
     * 从下载地址获取文件流(如果链接出现双斜杠，请用OKHttp)
     * @param url
     * @return
     */
//    public static ByteArrayOutputStream getDownloadFileStream(String url) {
//        String infoMessage = new StringBuilder().append("request getDownloadFileStream，url:").append(url).toString();
//        log.info(infoMessage);
//        ByteArrayOutputStream byteOutStream = null;
//        try {
//            CloseableHttpResponse response = httpClient.execute(new HttpGet(url));
//            log.info("getDownloadFileStream response status:{}", response.getStatusLine());
//            int statusCode = response.getStatusLine().getStatusCode();
//            if (statusCode == HttpStatus.SC_OK) {
//                //请求成功
//                HttpEntity entity = response.getEntity();
//                if (entity != null && entity.getContent() != null) {
//                    //复制输入流
//                    byteOutStream = cloneInputStream(entity.getContent());
//                }
//            }
//            HttpClientUtils.closeQuietly(response);
//        } catch (Exception e) {
//            log.error(infoMessage + " failure", e);
//        }
//        return byteOutStream;
//    }


    /**
     * 发送http请求(通用方法)
     *
     * @param httpMethod 请求方式（GET、POST、PUT、DELETE）
     * @param url        请求路径
     * @param header     请求头
     * @param params     请求body（json数据）
     * @return 响应文本
     */
    public static String sendHttp(HttpMethod httpMethod, String url, Map<String, String> header, String params) {
//        String infoMessage = new StringBuilder().append("request sendHttp，url:").append(url).append("，method:").append(httpMethod.name()).append("，header:").append(JSONObject.toJSONString(header)).append("，param:").append(params).toString();
//        log.info(infoMessage);
        //返回结果
        String result = null;
        long beginTime = System.currentTimeMillis();
        try {
            ContentType contentType = ContentType.APPLICATION_JSON.withCharset("UTF-8");
            HttpRequestBase request = buildHttpMethod(httpMethod, url);
            if (Objects.nonNull(header) && !header.isEmpty()) {
                for (Map.Entry<String, String> entry : header.entrySet()) {
                    //打印头部信息
                    if (log.isDebugEnabled()) {
                        log.debug(entry.getKey() + ":" + entry.getValue());
                    }
                    request.setHeader(entry.getKey(), entry.getValue());
                }
            }
            if (null != params && !"".equals(params)) {
//            if (StringUtils.isNotEmpty(params)) {
                if (HttpMethod.POST.equals(httpMethod) || HttpMethod.PUT.equals(httpMethod)) {
                    ((HttpEntityEnclosingRequest) request).setEntity(new StringEntity(params, contentType));
                }
            }
            CloseableHttpResponse response = httpClient.execute(request);
            HttpEntity httpEntity = response.getEntity();
            log.info("sendHttp response status:{}", response.getStatusLine());
            if (Objects.nonNull(httpEntity) && response.getStatusLine().getStatusCode() == 200) {
                InputStream content = httpEntity.getContent();
                // 封装输入流is，并指定字符集
                BufferedReader br = new BufferedReader(new InputStreamReader(content, "UTF-8"));
                // 存放数据
                StringBuffer sbf = new StringBuffer();
                String temp = null;
                while ((temp = br.readLine()) != null) {
                    sbf.append(temp);
                }
                result = sbf.toString();

//                // 将内容转化成IO流
//                InputStream content = httpEntity.getContent();
//                // 输入流转换成字符串
//                byte[] byteArr = new byte[content.available()];
//                content.read(byteArr);  // 将输入流写入到字节数组中
//                String resultRes = new String(byteArr);
//                System.out.println("Successfully：" + resultRes);


//                result = EntityUtils.toString(httpEntity, "UTF-8");
                log.info("sendHttp response body:{}", result);
            }
            HttpClientUtils.closeQuietly(response);//关闭返回对象
        } catch (Exception e) {
            e.printStackTrace();
//            log.error(infoMessage + " failure", e);
        }
        long endTime = System.currentTimeMillis();
        log.info("request sendHttp response time cost:" + (endTime - beginTime) + " ms");
        return result;
    }

    /**
     * 请求方法（全大些）
     */
    public enum HttpMethod {
        GET, POST, PUT, DELETE
    }

    /**
     * 上传返回结果封装
     */
    public static class RequestResult {
        private boolean isSuccess;//是否成功
        private String msg;//消息

        public boolean isSuccess() {
            return isSuccess;
        }

        public RequestResult setSuccess(boolean success) {
            isSuccess = success;
            return this;
        }

        public String getMsg() {
            return msg;
        }

        public RequestResult setMsg(String msg) {
            this.msg = msg;
            return this;
        }

        public RequestResult() {
            this.isSuccess = false;
        }
    }

    /**
     * 构建请求方法
     *
     * @param method
     * @param url
     * @return
     */
    private static HttpRequestBase buildHttpMethod(HttpMethod method, String url) {
        if (HttpMethod.GET.equals(method)) {
            return new HttpGet(url);
        } else if (HttpMethod.POST.equals(method)) {
            return new HttpPost(url);
        } else if (HttpMethod.PUT.equals(method)) {
            return new HttpPut(url);
        } else if (HttpMethod.DELETE.equals(method)) {
            return new HttpDelete(url);
        } else {
            return null;
        }
    }

    /**
     * 配置证书
     *
     * @return
     */
    private static SSLContext createSSLContext() {
        try {
            //信任所有,支持导入ssl证书
            TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
            return sslContext;
        } catch (Exception e) {
            log.error("初始化ssl配置失败", e);
            throw new RuntimeException("初始化ssl配置失败");
        }
    }

    /**
     * 绕过验证
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
//        SSLContext sc = SSLContext.getInstance("SSLv3");
        SSLContext sc = SSLContext.getInstance("TLSv1.2");
        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sc.init(null, new TrustManager[]{trustManager}, null);
        return sc;
    }

    /**
     * 复制文件流
     * @param input
     * @return
     */
//    private static ByteArrayOutputStream cloneInputStream(InputStream input) {
//        try {
//            ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
//            byte[] buffer = new byte[1024];
//            int len;
//            while ((len = input.read(buffer)) > -1) {
//                byteOutStream.write(buffer, 0, len);
//            }
//            byteOutStream.flush();
//            return byteOutStream;
//        } catch (IOException e) {
//            log.warn("copy InputStream error，{}", ExceptionUtils.getStackTrace(e));
//        }
//        return null;
//    }

    /**
     * 输入流转字节流
     *
     * @param
     * @return
     */
//    private static byte[] inputStream2byte(InputStream in) {
//        ByteArrayOutputStream bos = null;
//        try {
//            bos = new ByteArrayOutputStream();
//            byte[] b = new byte[1024];
//            int n;
//            while ((n = in.read(b)) != -1) {
//                bos.write(b, 0, n);
//            }
//            in.close();
//            bos.close();
//            byte[] buffer = bos.toByteArray();
//            return buffer;
//        } catch (IOException e) {
//            log.warn("inputStream transfer byte error，{}", ExceptionUtils.getStackTrace(e));
//        } finally {
//            try {
//                if (in != null) {
//                    in.close();
//                }
//            } catch (IOException e) {
//                log.error("clone inputStream error", e);
//            }
//            try {
//                if (bos != null) {
//                    bos.close();
//                }
//            } catch (IOException e) {
//                log.error("clone outputStream error", e);
//            }
//        }
//        return null;
//    }
    public static String getUrlSkipSSL(String requestUrl) throws KeyManagementException, NoSuchAlgorithmException, IOException {
        String body = "";
        //采用绕过验证的方式处理https请求
        SSLContext sslcontext = createIgnoreVerifySSL();
        //设置协议http和https对应的处理socket链接工厂的对象
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", new SSLConnectionSocketFactory(sslcontext))
                .build();
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

        RequestConfig defaultConfig = RequestConfig.custom()
                .setCookieSpec(CookieSpecs.STANDARD).build();
        //创建自定义的httpclient对象
        CloseableHttpClient client = HttpClients.custom()
                .setConnectionManager(connManager)
                .setDefaultRequestConfig(defaultConfig)
                .build();
        try {
            //创建get方式请求对象
            HttpGet get = new HttpGet(requestUrl);
            //指定报文头Content-type、User-Agent
            get.setHeader(":authority","item-soa.jd.com");
            get.setHeader(":method","GET");
            get.setHeader("accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
            get.setHeader("Content-type", "application/x-www-form-urlencoded");
            get.setHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36");
            String cookie="__jdu=16285011738421400614125; shshshfpa=ef9c238d-bc43-d9a3-69cc-52bce536cfa1-1628732229; shshshfpb=eEgZZ3iz7Y6CW5UV8dXZnQg%3D%3D; pinId=n8FNRkO5uxBXP7YWv6mDFbV9-x-f3wj7; user-key=358cac15-ef90-40c7-9bac-f03bf53ba251; pin=266971738-289848; unick=%E4%BC%BC%E6%B0%B4%E4%BC%BC%E4%BC%BC%E6%B5%81%E5%B9%B4; _tp=5skolYgkLJeZqJiL%2FHiryU%2BV1l6ToozZWCSLN3%2Bt4hw%3D; _pst=266971738-289848; __jdv=122270672|baidu|-|organic|notset|1647258971604; TrackID=1JMK0CvAQKislUmeD8IOxhLCXb-6DLZyODEYp1bSkzv14qnvq9VWnGrUp1lN-EHzvRzBglGyteAQKwNhg7BeMWqUHT6RKxlgo6UuihFp8hyCdYE_-crIcJGI6YA09n5wq; sso.jd.com=BJ.28C9C198FDA0B749514C295549E51C9E0620220317100631; PCSYCityID=CN_110000_110100_0; areaId=1; ipLoc-djd=1-2802-54745-0; token=3cceec57b78d09edddd9b92680b66b80,2,915288; __tk=fttMzxk61vx4yy2lqxNwM0b6qxT4MyP0fty0gUbEwvq4yzaAqts3MATYwwx4gBCaqxbRNYbl,2,915288; shshshfp=a9e8d41a1594ace264e6acc5e44d18d7; shshshsID=9f1f0c2a3ab36342bf8b8f78f82a8df5_7_1647518460996; __jda=122270672.16285011738421400614125.1628501174.1647508588.1647517041.104; __jdb=122270672.7.16285011738421400614125|104.1647517041; __jdc=122270672; ip_cityCode=2802; 3AB9D23F7A4B3C9B=R65HNDCYCS6FXHOVUNKOR273L3FZOQDZADP5LCGX4Y2MIKAQBHGFJUAYOVDAQDOTABAF23JMVYARPRHA4SJBSAKQLI";
            get.setHeader("cookie",cookie);
            //执行请求操作，并拿到结果（同步阻塞）
            CloseableHttpResponse response = client.execute(get);
            //获取结果实体
            HttpEntity entity = response.getEntity();
            if (Objects.nonNull(entity) && response.getStatusLine().getStatusCode() == 200) {
                //按指定编码转换结果实体为String类型
                body = EntityUtils.toString(entity, "UTF-8");
            } else {
                return null;
            }
            EntityUtils.consume(entity);
            //释放链接
            response.close();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            client.close();
        }
        return body;
    }
}
